package org.hzz.snake;

import static org.hzz.config.SnakeConstants.HEIGHT;
import static org.hzz.config.SnakeConstants.SPEED;
import static org.hzz.config.SnakeConstants.UNIT_SIZE;
import static org.hzz.config.SnakeConstants.WIDTH;

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.Map;

import javax.swing.JOptionPane;
import javax.swing.Timer;

import lombok.extern.java.Log;


/**
 * 贪吃蛇游戏的基本逻辑
 */

 /**
  * SnakeController 类实现了 ActionListener 接口，用于处理定时器触发的事件。
在构造方法中，将 SnakeModel 和 SnakeView 对象传入，并设置了视图的模型、设置视图可以获取焦点，并添加了键盘事件监听器。
startGame() 方法用于开始游戏，初始化游戏并启动定时器。
actionPerformed() 方法是 ActionListener 接口的实现方法，在定时器触发时调用，用于处理游戏逻辑，包括移动贪吃蛇、检查碰撞、重新绘制界面等操作。
move() 方法用于移动贪吃蛇，根据当前方向移动贪吃蛇的头部，并根据是否吃到食物来添加新的头部或移除尾部。
checkCollisions() 方法用于检查碰撞，包括是否撞墙和是否撞到自己的身体。
MyKeyAdapter 是一个内部类，继承自 KeyAdapter，用于处理键盘事件，根据按键设置贪吃蛇的移动方向。
  */
@Log
public class SnakeController implements ActionListener{

    private SnakeModel snakeModel;
    private SnakeView snakeView;
    private Timer timer;

    public SnakeController(SnakeModel snakeModel,SnakeView snakeView){

        this.snakeModel = snakeModel;
        this.snakeView = snakeView;

        this.snakeView.setSnakeModel(this.snakeModel);
        // 设置视图可以获取焦点
        /**
         *  这行代码是将 SnakeView 视图设置为可获得焦点。在 Java Swing 中，组件默认是不可获得焦点的，
         * 这意味着它们不会直接接收键盘事件。通过调用 setFocusable(true) 方法，可以将视图设置为可获得
         * 焦点，这样当用户与视图交互时，视图就可以接收键盘事件了。
         * 在贪吃蛇游戏中，这行代码的作用是确保当用户想要控制贪吃蛇移动时，视图能够接收到键盘事件，从而改变贪吃蛇的移动方向。
         */
        this.snakeView.setFocusable(true);
        // 监听键盘按键设置贪吃蛇移动方向
        this.snakeView.addKeyListener(new HandleSnakeKeyAdpater());
    }

    // 开始游戏
    public void startGame(){
        log.info("开始游戏");
        snakeModel.newGame();
        timer = new Timer(SPEED, this);
        timer.start();
    }

    // 在定时器触发时调用，用于处理游戏逻辑，包括移动贪吃蛇、检查碰撞、重新绘制界面等操作。
    @Override
    public void actionPerformed(ActionEvent e) {
        log.info("定时器触发");
        if(snakeModel.isRunning()){
            move(); // 移动
            checkCollisions();  // 检查碰撞
            snakeView.repaint();    // 重新绘制
        }
    }

    private void move(){
        List<Point> snakeParts 
                        = snakeModel.getSnakeParts();
        Point header = snakeParts.get(0);
        Point newHeader = (Point)header.clone();

        // Java13开始的 switch expression
        // 更新位置，为了渲染
        switch (snakeModel.getDirection()) {
            case 'U' -> newHeader.y -= UNIT_SIZE;
            case 'D' -> newHeader.y += UNIT_SIZE;
            case 'L' -> newHeader.x -= UNIT_SIZE;
            case 'R' -> newHeader.x += UNIT_SIZE;
        }

        // 更新贪吃蛇的身体：这里很巧妙，直接是添加头部
        snakeParts.add(0,newHeader);
        // 如果吃到食物，生成新的食物；否则移除尾部
        if(newHeader.equals(snakeModel.getFood())){ 
            snakeModel.spawnFood();
        }else{  // 没有吃到
           snakeParts.remove(snakeParts.size() - 1); 
        }
    }

    // 检查碰撞: 从蛇的头部入手
    private void checkCollisions(){
        List<Point> snakeParts = snakeModel.getSnakeParts();
        Point header = snakeParts.get(0);

        // 检查是否碰墙壁
        if(header.x < 0 || header.x > WIDTH
                || header.y < 0 || header.y > HEIGHT){
            // 停止
            snakeModel.setRunning(false);
            JOptionPane.showMessageDialog(snakeView, "Game Over! You hit the wall.");
            return;
        }

        // 检查是否碰撞到自己的身体
        for(int i = 1; i < snakeParts.size(); i++){
            if(header.equals(snakeParts.get(i))){
                snakeModel.setRunning(false);
                JOptionPane.showMessageDialog(snakeView, "Game Over! You hit yourself.");
                return;
            }
        }
    }


    /**
     * 处理键盘事件的内部类
     */
    private class HandleSnakeKeyAdpater extends KeyAdapter{

        @Override
        public void keyPressed(KeyEvent e) {
            char key = e.getKeyChar();
            if (key == 'W' || key == 'w' || e.getKeyCode() == KeyEvent.VK_UP) {
                updateDirection('U'); // 上
            } else if (key == 'S' || key == 's' || e.getKeyCode() == KeyEvent.VK_DOWN) {
                updateDirection('D'); // 下
            } else if (key == 'A' || key == 'a' || e.getKeyCode() == KeyEvent.VK_LEFT) {
                updateDirection('L'); // 左
            } else if (key == 'D' || key == 'd' || e.getKeyCode() == KeyEvent.VK_RIGHT) {
                updateDirection('R'); // 右
            }
        }
        
        // 使用 Map.of（Java 9+）
        Map<Character,Integer> maps = Map.of('U', -1,'D',1,'L',-2,'R',2);
        // 更新方向
        private void updateDirection(char direction){
            // 比如：往上的走的时候只能往左右走
            if( snakeModel.getSnakeParts().size() != 1){    // 忽略掉一个元素
                if(maps.get(direction) + maps.get(snakeModel.getDirection()) == 0 ){
                    return;
                }
            }
            snakeModel.setDirection(direction);

        }
    }
}
